package org.enwey.icarenew.API;

import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.kollway.android.imagecachelib.ImageCacheManager;

import org.enwey.icarenew.APPUtils.BaseHttp;
import org.enwey.icarenew.Constant;
import org.enwey.icarenew.Manager.LoginManager;
import org.enwey.icarenew.Manager.SettingsManager;
import org.enwey.icarenew.Model.Comment;
import org.enwey.icarenew.Model.New;
import org.enwey.icarenew.Model.NewStyle;
import org.enwey.icarenew.Model.Topic;
import org.enwey.icarenew.Model.User;
import org.enwey.icarenew.MyApplication;

import java.io.File;
import java.lang.ref.WeakReference;
import java.lang.reflect.Type;
import java.util.Hashtable;
import java.util.IllegalFormatException;
import java.util.Set;

/**
 * Created by enwey on 2014/11/25.
 */
public class APIManager {
    public static final int REQUEST_TIMEOUT_SECONDS = 15;
    public static final String HOST = "icarenew.sinaapp.com";
    public static final String BASE_URL = "http://" + HOST;
    public static final String KEY_PAGE_SIZE = "pageSize";
    public static final int PAGE_SIZE = 15;
    private static final String TAG = "APIManager";

    private static APIManager instance;
    private static WeakReference<Context> weakAppContext;
    private RequestQueue requestQueue;
    private ImageLoader imageLoader;

    public APIManager(Context context) {
        Context appContext = context.getApplicationContext();
        weakAppContext = new WeakReference<>(appContext);
        requestQueue = Volley.newRequestQueue(appContext);
    }

    public static APIManager getInstance(Context context) {
        if (weakAppContext == null || weakAppContext.get() == null) {
            instance = null;
        }
        if (instance == null) {
            synchronized (APIManager.class) {
                // why decide again
                if (instance == null) {
                    instance = new APIManager(context);
                }
            }
        }
        return instance;
    }

    public static String toAbsoluteUrl(String input) {
        if (TextUtils.isEmpty(input)) {
            return BASE_URL;
        }
        if (input.startsWith("http://")) {
            return input;
        }
        if (!input.startsWith("/")) {
            input = "/" + input;
        }
        return "http://" + HOST + input;
    }

    public RequestQueue getRequestQueue() {
        return requestQueue;
    }

    private String getParameterDescription(String... parameterKey) {
        StringBuilder stringBuilder = new StringBuilder("your parameters map must be look like this: ");
        stringBuilder.append("{ ");
        for (String item : parameterKey) {
            stringBuilder.append(item);
            stringBuilder.append("=? ");
        }
        stringBuilder.append("}");

        return stringBuilder.toString();
    }

    private <T> RequestAction<T> buildRequestAction(Type modelType, String api, int httpMethod, Hashtable<String, String> parameters, Response.ErrorListener errorListener, Response.Listener<T> successListener) {

        return buildRequestAction(modelType, api, httpMethod, parameters, null, true, errorListener, successListener);
    }

    private <T> RequestAction<T> buildRequestAction(Type modelType,
                                                    String api,
                                                    int httpMethod,
                                                    Hashtable<String, String> parameters,
                                                    RequestAction.RequestBody body,
                                                    boolean runNow,
                                                    Response.ErrorListener errorListener,
                                                    final Response.Listener<T> successListener) {

        Response.Listener<T> mySuccessListener = new Response.Listener<T>() {
            @Override
            public void onResponse(T response) {
                successListener.onResponse(response);
            }
        };
        RequestAction<T> requestAction = new RequestAction<T>(
                httpMethod,
                this.toAbsoluteUrl(api),
                null,
                modelType,
                body,
                errorListener,
                mySuccessListener
        );
        if (parameters != null) {
            Set<String> keySet = parameters.keySet();
            for (String key : keySet) {
                String value = parameters.get(key);
                requestAction.addRequestParams(new RequestParam(key, value));
            }
        }
        if (runNow) {
            getRequestQueue().add(requestAction);
        }
        return requestAction;
    }

    public ImageLoader getImageLoader() {
        if (imageLoader == null) {
            synchronized (this) {
                if (imageLoader == null && weakAppContext != null && weakAppContext.get() != null) {
//                    ImageCacheManager imageCacheManager = ImageCacheManager.getInstance();
//                    imageCacheManager.init(weakAppContext.get(), "images", CACHE_SIZE, Bitmap.CompressFormat.PNG, 80);
                    imageLoader = ImageCacheManager.getInstance().getImageLoader();
                }
            }
        }
        return imageLoader;
    }

    private <T> void checkRequestResult(T response) {
        Context context = weakAppContext.get();
        if (context == null) {
            return;
        }

        int responseCode;
        if (response instanceof RequestListResult) {
            responseCode = ((RequestListResult) response).code;

        } else if (response instanceof RequestResult) {
            responseCode = ((RequestResult) response).code;

        } else {
            return;
        }

        switch (responseCode) {
            case 1: {
                Intent intent = new Intent(Constant.RECEIVER_NO_LOGIN);
                LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
                LoginManager.getInstance(context).deleteLoginSession();
                SettingsManager.destroyLogin();
                break;
            }
            default:
                break;
        }
    }

    /**
     * login
     *
     * @param parameters
     * @param errorListener
     * @param successListener
     * @return
     */
    public RequestAction<RequestResult<User>> userLogin(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestResult<User>> successListener) {
        String ACCOUNT = "account";
        String PASSWORD = "password";

        if (parameters == null || !parameters.containsKey(ACCOUNT) || !parameters.containsKey(PASSWORD)) {
            throw new IllegalArgumentException(getParameterDescription(ACCOUNT, PASSWORD));
        }

        Type type = new TypeToken<RequestResult<User>>() {
        }.getType();
        return buildRequestAction(type, "api/user/login/", Request.Method.POST, parameters, errorListener, successListener);
    }

    /**
     * register
     *
     * @param parameters
     * @param errorListener
     * @param successListener
     * @return
     */
    public RequestAction<RequestResult<User>> registerUser(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestResult<User>> successListener) {
        String ACCOUNT = "account";
        String PASSWORD = "password";

        if (parameters == null || !parameters.containsKey(ACCOUNT) || !parameters.containsKey(PASSWORD)) {
            throw new IllegalArgumentException(getParameterDescription(ACCOUNT, PASSWORD));
        }

        Type type = new TypeToken<RequestResult<User>>() {
        }.getType();
        return buildRequestAction(type, "api/user/register/", Request.Method.POST, parameters, errorListener, successListener);
    }

    /**
     * update password
     *
     * @param parameters
     * @param errorListener
     * @param successListener
     * @return
     */
    public RequestAction<RequestResult<User>> updatePW(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestResult<User>> successListener) {
        String ACCOUNT = "account";
        String PASSWORD = "password";

        if (parameters == null || !parameters.containsKey(ACCOUNT) || !parameters.containsKey(PASSWORD)) {
            throw new IllegalArgumentException(getParameterDescription(ACCOUNT, PASSWORD));
        }

        Type type = new TypeToken<RequestResult<User>>() {
        }.getType();
        return buildRequestAction(type, "api/user/updatepw/", Request.Method.POST, parameters, errorListener, successListener);
    }

    public RequestAction<RequestResult<User>> completeInfo(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestResult<User>> successListener) {
        String ACCOUNT = "account";
//        String PASSWORD = "password";
        String NICK = "nick";
        String SUMMARY = "summary";

        if (parameters == null || !parameters.containsKey(ACCOUNT) || !parameters.containsKey(NICK) || !parameters.containsKey(SUMMARY)) {
            throw new IllegalArgumentException(getParameterDescription(ACCOUNT, NICK, SUMMARY));
        }

        Type type = new TypeToken<RequestResult<User>>() {
        }.getType();
        return buildRequestAction(type, "api/user/updateinfo/", Request.Method.POST, parameters, errorListener, successListener);
    }

    /**
     * getNewStyle
     *
     * @param parameters
     * @param errorListener
     * @param successListener
     * @return
     */
    public RequestAction<RequestListResult<NewStyle>> getNewStyle(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestListResult<NewStyle>> successListener) {

        Type type = new TypeToken<RequestListResult<NewStyle>>() {
        }.getType();
        return buildRequestAction(type, "api/new/newstyle/", Request.Method.POST, parameters, errorListener, successListener);
    }

    /**
     * userInfo
     *
     * @param parameters
     * @param errorListener
     * @param successListener
     * @return
     */
    public RequestAction<RequestResult<User>> userInfo(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestResult<User>> successListener) {
        String ACCOUNT = "account";
        String PASSWORD = "password";

        if (parameters == null || !parameters.containsKey(ACCOUNT) || !parameters.containsKey(PASSWORD)) {
            throw new IllegalArgumentException(getParameterDescription(ACCOUNT, PASSWORD));
        }

        Type type = new TypeToken<RequestResult<User>>() {
        }.getType();
        return buildRequestAction(type, "api/new/homepage/", Request.Method.POST, parameters, errorListener, successListener);
    }

    /**
     * doSupport
     *
     * @param parameters
     * @param errorListener
     * @param successListener
     * @return
     */
    public RequestAction<RequestResult<User>> doSupport(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestResult<User>> successListener) {
        String ACCOUNT = "account";
        String SUPPORT = "support";

        if (parameters == null || !parameters.containsKey(ACCOUNT) || !parameters.containsKey(SUPPORT)) {
            throw new IllegalArgumentException(getParameterDescription(ACCOUNT, SUPPORT));
        }

        Type type = new TypeToken<RequestResult<User>>() {
        }.getType();
        return buildRequestAction(type, "api/user/support/", Request.Method.POST, parameters, errorListener, successListener);
    }

    /**
     * getNewList
     *
     * @param parameters
     * @param errorListener
     * @param successListener
     * @return
     */
    public RequestAction<RequestListResult<New>> getNewList(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestListResult<New>> successListener) {
        String STYLE_ID = "style_id";
        String CURRENT_PAGE = "page";

        if (parameters == null || !parameters.containsKey(STYLE_ID) || !parameters.containsKey(CURRENT_PAGE)) {
            throw new IllegalArgumentException(getParameterDescription(STYLE_ID, CURRENT_PAGE));
        }

        Type type = new TypeToken<RequestListResult<New>>() {
        }.getType();
        return buildRequestAction(type, "api/new/newlist/", Request.Method.POST, parameters, errorListener, successListener);
    }

    /**
     * getTopicList
     *
     * @param parameters
     * @param errorListener
     * @param successListener
     * @return
     */
    public RequestAction<RequestListResult<Topic>> getTopicList(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestListResult<Topic>> successListener) {
        String CURRENT_PAGE = "page";

        if (parameters == null || !parameters.containsKey(CURRENT_PAGE)) {
            throw new IllegalArgumentException(getParameterDescription(CURRENT_PAGE));
        }

        Type type = new TypeToken<RequestListResult<Topic>>() {
        }.getType();
        return buildRequestAction(type, "api/new/topiclist/", Request.Method.POST, parameters, errorListener, successListener);
    }

    /**
     * getCommentList
     *
     * @param parameters
     * @param errorListener
     * @param successListener
     * @return
     *
     */
    public RequestAction<RequestListResult<Comment>> getCommentList(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestListResult<Comment>> successListener) {
        String CURRENT_PAGE = "page";


        if (parameters == null || !parameters.containsKey(CURRENT_PAGE)) {
            throw new IllegalArgumentException(getParameterDescription(CURRENT_PAGE));
        }

        Type type = new TypeToken<RequestListResult<Comment>>() {
        }.getType();
        return buildRequestAction(type, "api/new/commentlist/", Request.Method.POST, parameters, errorListener, successListener);
    }

    /**
     * getUserNewList
     *
     * @param parameters
     * @param errorListener
     * @param successListener
     * @return
     */
    public RequestAction<RequestListResult<Topic>> userNewList(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestListResult<Topic>> successListener) {
        String ACCOUNT = "account";

        if (parameters == null || !parameters.containsKey(ACCOUNT)) {
            throw new IllegalArgumentException(getParameterDescription(ACCOUNT));
        }

        Type type = new TypeToken<RequestListResult<Topic>>() {
        }.getType();
        return buildRequestAction(type, "api/new/usernewlist/", Request.Method.POST, parameters, errorListener, successListener);
    }

    public RequestAction<RequestResult<Topic>> writeTopic(Hashtable<String, String> parameters, Response.ErrorListener errorListener, final Response.Listener<RequestResult<Topic>> successListener) {
        String USER_ID = "user_id";
        String TITLE = "title";
        String CONTENT = "content";

        if (parameters == null || !parameters.containsKey(USER_ID) || !parameters.containsKey(TITLE) || !parameters.containsKey(CONTENT)) {
            throw new IllegalArgumentException(getParameterDescription(USER_ID, TITLE, CONTENT));
        }

        Type type = new TypeToken<RequestResult<Topic>>() {
        }.getType();
        return buildRequestAction(type, "api/new/writetopic/", Request.Method.POST, parameters, errorListener, successListener);
    }

    /**
     * newDetail
     *
     * @param parameters
     * @return
     */
    public String newDetail(Hashtable<String, String> parameters) {
        String STYLE_ID = "style_id";
        String NEW_ID = "new_id";

        if (parameters == null || !parameters.containsKey(STYLE_ID) || !parameters.containsKey(NEW_ID)) {
            throw new IllegalArgumentException(getParameterDescription(STYLE_ID, NEW_ID));
        }

        String styleId = parameters.get(STYLE_ID);
        String newId = parameters.get(NEW_ID);
        String path = BASE_URL + "/api/new/newdetail/index.php?style_id=" + styleId + "&new_id=" + newId;

        return path;
    }

    public String topicDetail(Hashtable<String, String> parameters) {
        String TOPIC_ID = "topic_id";

        if (parameters == null || !parameters.containsKey(TOPIC_ID)) {
            throw new IllegalArgumentException(getParameterDescription(TOPIC_ID));
        }

        String topicId = parameters.get(TOPIC_ID);
        String path = BASE_URL + "/api/new/topicdetail/index.php?topic_id=" + topicId;

        return path;
    }

    /**
     * 上传头像
     *
     * @param parameters      {filePath=?}
     * @param errorListener
     * @param successListener
     */
    public void updateHead(Hashtable<String, String> parameters,
                           Response.ErrorListener errorListener,
                           Response.Listener<RequestResult<?>> successListener) {
        String USER_ID = "user_id";
        String KEY_FILEPATH = "imgFile";
        if (parameters == null || !parameters.containsKey(KEY_FILEPATH) || !parameters.containsKey(USER_ID)) {
            throw new IllegalArgumentException(getParameterDescription(USER_ID, KEY_FILEPATH));
        }

        final String apiPath = "/api/uploadimage/";
        String urlString = BASE_URL + apiPath;
        Hashtable<String, File> imageFiles = new Hashtable<>();
        imageFiles.put("imgFile", new File(parameters.remove(KEY_FILEPATH)));
        uploadFiles(urlString, parameters, imageFiles, errorListener, successListener);
    }

    private void uploadFiles(final String urlString,
                             final Hashtable<String, String> parameters,
                             final Hashtable<String, File> imageFiles,
                             final Response.ErrorListener errorListener,
                             final Response.Listener<RequestResult<?>> successListener) {
        MyApplication.bgExecutor.submit(new Runnable() {

            @Override
            public void run() {
                Exception exception = null;
                RequestResult<?> result = null;
                try {
                    Gson gson = new Gson();
//                    Hashtable<String, String> headers = new Hashtable<>();
//                    headers.put(RequestAction.COOKIE_KEY, RequestAction.SESSION_ID + "=" + loginManager.getLoginSession());
                    String responseJSON = BaseHttp.postFormWithFiles(urlString, null, parameters, imageFiles);
                    result = gson.fromJson(responseJSON, RequestResult.class);
                } catch (Exception e) {
                    exception = e;
                    e.printStackTrace();
                } finally {
                    final RequestResult<?> finalResult = result;
                    final Exception finalException = exception;

                    MyApplication.uiHandler.post(new Runnable() {

                        @Override
                        public void run() {
                            if (finalResult != null) {
                                successListener.onResponse(finalResult);
                                checkRequestResult(finalResult);
                            } else {
                                String exceptionMessage = (finalException != null) ? finalException.getMessage() : "";
                                errorListener.onErrorResponse(new VolleyError(exceptionMessage));
                            }
                        }
                    });
                }

            }
        });
    }
}
